import uuid
from   google.appengine.ext    import db

#========================================================================================
# ironcap.data.nodes.ReferenceNode
#========================================================================================
class ReferenceNode(db.Model):
	
	""" A Reference Node is a node which references its children by way of SelfReferenceProperties """
	
	parent_node = db.SelfReferenceProperty (indexed=True)
	ordinal     = db.IntegerProperty       (indexed=True, default=0)
	
	@classmethod
	def create(cls, parent_node=None, **kwds):
		""" creates a new node """
		
		# create node
		ret     = None
		if parent_node:
			ret = cls(key_name = str(uuid.uuid4()), parent_node = parent_node.key())	
		else:
			ret = cls(key_name = str(uuid.uuid4()))	
		
		# apply kwds
		for k,v in kwds.iteritems():
			setattr(ret, k, v)

		ret.put()
		return ret

	@property
	def children(self):
		""" returns children nodes for the current node """
		return db.Query( self.__class__ ).filter( 'parent_node', self.key() ).order('ordinal')

	@property
	def siblings(self):
		""" returns sibling nodes for the current node including the current node """
		if self.parent_node:
			return db.Query( self.__class__ ).filter( 'parent_node', self.parent_node.key() ).order('ordinal')
		else:
			return db.Query( self.__class__ ).filter( 'parent_node', None ).order('ordinal')
	
	@property
	def descendants(self):
		""" expands all child nodes under this node and includes itself """
		result  = []
		stack   = []
		stack.append(self)
		while len(stack) > 0:
			current = stack.pop()
			result.append(current)
			for n in current.children:
				stack.append(n)
		return result
		
	@property
	def ancestors(self):
		""" returns this nodes parent nodes, order is from root node to self """
		list = []
		list.insert(0, self)
		current = self
		while current.parent_node:
			current = current.parent_node
			list.insert(0 , current)
		return list	
	
	def update(self, **kwds):
		# apply kwds
		for k,v in kwds.iteritems():
			setattr(self, k, v)
		self.put()	
	
	def delete(self): 
		""" deletes this node and its children """
		for n in self.descendants:
			db.delete(n)
			
#========================================================================================
# ironcap.data.nodes.GroupNode
#========================================================================================
class GroupNode(db.Model):
	
	""" A GroupNode is a node that stores itself children part of an entity group
		so that the nodes become transactable """
	ordinal     = db.IntegerProperty       (indexed=True, default=0)
	depth       = db.IntegerProperty       (indexed=True, default=0)
	
	@classmethod
	def create(cls, parent_node=None, **kwds):
		""" creates a new node """
		# create node
		ret = cls(key_name = str(uuid.uuid4()), parent = parent_node)	
		
		# apply kwds
		for k,v in kwds.iteritems():
			setattr(ret, k, v)
		
		# create depth (default is 0)
		if parent_node:
			ret.depth = parent_node.depth + 1
		
		ret.put()
		return ret
	
	@classmethod
	def get_by_url(cls, url):
		return db.Query( cls ).filter( 'url', url ).get()
	
	@property
	def children(self):
		""" returns children nodes for the current node """
		return db.Query( self.__class__ ).ancestor( self.key() ).filter('depth', self.depth + 1).order('ordinal')

	@property
	def siblings(self):
		""" returns sibling nodes for the current node including the current node """
		if self.parent():
			return db.Query( self.__class__ ).ancestor( self.parent().key() ).filter('depth', self.depth).order('ordinal')
		else:
			return db.Query( self.__class__ ).filter( 'depth', 0 ).order('ordinal')
	
	@property
	def descendants(self):
		""" expands all child nodes under this node and includes itself """
		result  = []
		stack   = []
		stack.append(self)
		while len(stack) > 0:
			current = stack.pop()
			result.append(current)
			for n in current.children:
				stack.append(n)
		return result
		
	@property
	def ancestors(self):
		""" returns this nodes parent nodes, order is from root node to self """
		list = []
		list.insert(0, self)
		current = self
		while current.parent():
			current = current.parent()
			list.insert(0 , current)
		return list	
	
	def update(self, **kwds):
		# apply kwds
		for k,v in kwds.iteritems():
			setattr(self, k, v)		
		self.put()
	
	def delete(self): # need to potentially use task queues on large trees.
		""" deletes this node and its children """
		def txn():
			for n in self.descendants:
				db.delete(n)
		db.run_in_transaction(txn)